package com.winning.gjhlht.ihe.cdaService.controller;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

import com.winning.gjhlht.bean.InterfaceConfigBean;
import com.winning.gjhlht.ihe.cdaService.service.CdaDocumentInterface;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.framework.server.manager.SQLConfigLoader;
import com.framework.util.date.DateConvert;
import com.winning.common.Constans;
import com.winning.common.data.service.PlatBasicDataService;
import com.winning.common.tools.SQLTool;
import com.winning.core.job.BaseJob;
import com.winning.gjhlht.ihe.cdaService.service.IBaseCDACreaterServiceInter;
import com.winning.gjhlht.ihe.job.thread.IHECreateCDARequestTask;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("/CDAController")
public class CDAController {
//    private static Logger LOGGER = Logger.getLogger("CDAController.class");
    private static Logger LOGGER = Logger.getLogger(Constans.LOG4J_PBS_LOGGER_ID);


    @Autowired
    public IBaseCDACreaterServiceInter baseCDACreaterService = null;

    @Autowired
    public PlatBasicDataService platBasicDataService = null;

    @Autowired
    CdaDocumentInterface cdaDocumentInterface;
    //数据源
    private static String dbDataSourceID = "Default_SQL_Dev";

    //增量更新表的标识
    private static String serviceID = "CDA_BUFFER_01";


    /**
     * 测试接口
     */
    @RequestMapping(value = "/startCDATest")
    @ResponseBody
    public String startTest() {

        List<InterfaceConfigBean> analysis = analysis();

        for (int i = 0; i < analysis.size(); i++) {
            cdaDocumentInterface.createCDATest(analysis.get(i));
        }
        LOGGER.info("CDA文档生成结束....");

        return "Success";

    }

    /**
     * 解析配置文件中的接口配置
     */
    private static List<InterfaceConfigBean> analysis() {

        List<InterfaceConfigBean> list = new ArrayList<>();

        try {
            SAXReader reader = new SAXReader();

            URL resource = IHECreateCDARequestTask.class.getResource("/config/interfaceConfig.xml");

            if (resource == null) {
                LOGGER.error("CDA接口配置文件\"interfaceConfig.xml\"不存在");
                return null;
            }

            //解决文件不存在的空指针
            Document document = reader.read(resource);

            Element root = document.getRootElement();
            List<Element> elements = root.elements("item");
            InterfaceConfigBean bean;
            for (Element node : elements) {
                Class<InterfaceConfigBean> configBeanClass = InterfaceConfigBean.class;


                bean = new InterfaceConfigBean();
                bean.setId(node.element("id").getTextTrim());
                bean.setCategory(node.element("category").getTextTrim());
                bean.setCdaTempFileName(node.element("cdaTempFileName").getTextTrim());
                bean.setSQLByID(node.element("SQLByID").getTextTrim());
                bean.setJobEnable(node.element("jobEnable").getTextTrim().equalsIgnoreCase("true"));
                bean.setName(node.element("name").getTextTrim());
                //只将配置中开启的接口加入集合
                if (bean.getJobEnable()) {
                    list.add(bean);
                }

            }
            return list;

        } catch (Exception e) {
            LOGGER.info("CDA接口配置文件解析失败");
            throw new RuntimeException();
        }

    }

    /**
     * 测试生成CDA文档的接口
     * 方法名称: startCreateCDATest
     * void  返回类型说明
     *
     * @author liuwensheng@winning.com.cn<br/>
     * 创建时间：2017-1-11/下午3:56:42<br/>
     */
//    @RequestMapping(value = "/startCreateCDATest")
    public void startCreateCDATest() {

        String queryGRZCJGSQL = SQLConfigLoader.getSQLByID("IHE_QUERY_GR_JBXX_CDA_EHR");

        //1、从基础SQL中按照规则提取增量更新表名称，规则为TABLENAME_TBRQ，TBRQ是平台所有表必须有的字段
        HashMap<String, String> tableNameMap = SQLTool.getTableNameFromSQL(queryGRZCJGSQL);

        //2、获取业务库中保存的增量更新时间
        HashMap<String, String> tableValueMapFromDB = platBasicDataService.getTableIncreaseTimeMapFromDB();

        //3、增加增量流程的SQL
        String increaseSQLStr = SQLTool.createIncreaseSQL(serviceID, queryGRZCJGSQL, tableNameMap, tableValueMapFromDB);

        LOGGER.info("-->基础配置SQL为:[" + queryGRZCJGSQL + "]");

        LOGGER.info("-->增量更新处理后SQL为:[" + increaseSQLStr + "]");

        //4、统计SQL
        String countSQL = "SELECT " + "COUNT(1) AS " + BaseJob.COLUMN_COUNT + " FROM (" + increaseSQLStr + ") A";

        LOGGER.info("-->查询总数SQL为:[" + countSQL + "]");

        //总共查询出多少条数据量, 多数据源支持
        int totalDataCount = platBasicDataService.getPlatBasicDataCountHandler(countSQL, BaseJob.COLUMN_COUNT,
                dbDataSourceID);

        LOGGER.info("-->总数据量为:[" + totalDataCount + "]");

        //【2】、计算需要创建的线程数量
        int jobsNumber = 1;

        //一批次上传的数据量，也可理解为一页的数量
        int perSize = 1000;

        jobsNumber = totalDataCount / perSize;

        if (totalDataCount % perSize > 0) {
            jobsNumber += 1;
        }

        //批次分页页码
        int pageIndexNum = 0;

        //分批次执行上传
        for (int pcStartIndex = 0; pcStartIndex < jobsNumber; pcStartIndex++) {
            //每次都会在sql中增加最新的增量时间，每次都应该是从第一页开始
            List<Map<String, Object>> registrationRequestInfoDataList = platBasicDataService
                    .getPlatBasicDataByIncreaseTimeHandler(dbDataSourceID, serviceID, queryGRZCJGSQL, pageIndexNum,
                            perSize);

            if (registrationRequestInfoDataList == null || registrationRequestInfoDataList.size() == 0) {
                LOGGER.info("--> 没有数据");
            } else {
                int result = createIHERegistrationRequestTaskHandler(registrationRequestInfoDataList, queryGRZCJGSQL);
                pageIndexNum += 1;
                if (result > 0) {
                    /* 返回1，是因为，业务发生时间和数据库记录的增量时间相同，表示一个时间段内发生的数据量太大（同一时间内的数据量远大于一页）
                     * 第二批次查询数据就必须向后翻一页。
                     */
                    pageIndexNum += 1;
                } else {
                    //返回0，是因为，SQL每次都会做增量时间操作，每次的查询都是根据最新的时间筛选，所以每次的查询结果集都应该是从第一页开始。
                    pageIndexNum = 0;
                }
            }

        }

    }

    /**
     * 创建线程批量生成CDA文档
     * 方法名称: createIHERegistrationRequestTaskHandler
     *
     * @param
     * @return int  返回类型说明<br/>
     * @throws <br/>
     * @author zq@winning.com.cn<br/>
     * 创建时间：2017-1-14/下午12:06:44<br/>
     */
    private int createIHERegistrationRequestTaskHandler(List<Map<String, Object>> registrationRequestInfoDataList,
                                                        String basicSQL) {

        //单个线程可执行的上传数据量
        int baseNumber = 100;

        //计算处理所有数据需要启动的线程数量
        int totalNumber = registrationRequestInfoDataList.size();
        int threadNumber = totalNumber / baseNumber;

        if (totalNumber % baseNumber > 0) {
            threadNumber += 1;
        }

        List<IHECreateCDARequestTask> dataSetSubmitThreadList = new ArrayList<IHECreateCDARequestTask>();

        //创建提交数据线程
        for (int j = 0; j < threadNumber; j++) {
            int fromIndex = j * baseNumber;
            int toIndex = (j + 1) * baseNumber;

            List<Map<String, Object>> tempRowListValue = new ArrayList<Map<String, Object>>();

            //截取一段数据
            for (int index = fromIndex; index < toIndex; index++) {
                if (index < registrationRequestInfoDataList.size()) {
                    tempRowListValue.add(registrationRequestInfoDataList.get(index));
                }
            }

            Timer timer = new Timer();

            //创建执行上传数据接口的线程
            IHECreateCDARequestTask dataSetSubmitThread = new IHECreateCDARequestTask(tempRowListValue,
                    baseCDACreaterService, dbDataSourceID);
            timer.schedule(dataSetSubmitThread, 0);

            //保存所有的线程
            dataSetSubmitThreadList.add(dataSetSubmitThread);
        }

        //********************* 2、检查所有子线程的执行结果，一批提交接口线程数据执行结果 *********************
        boolean isAllOver = false;

        //当前线程已经变换为监控线程，持续判断子线程的执行结果，如果所有子线程全部执行完毕，表示当前job执行结束。可做更新增量时间操作
        while (!isAllOver) {
            for (int threadIndex = 0; dataSetSubmitThreadList != null
                    && threadIndex < dataSetSubmitThreadList.size(); threadIndex++) {
                IHECreateCDARequestTask iheRegistrationRequestTask = dataSetSubmitThreadList.get(threadIndex);

                if (!iheRegistrationRequestTask.isOver()) {
                    isAllOver = false;
                    break;
                }

                iheRegistrationRequestTask.cancel();
                iheRegistrationRequestTask = null;

                isAllOver = true;
            }

            try {
                //持续监控
                Thread.sleep(10);
            } catch (InterruptedException e) {
                //打印异常堆栈信息
                LOGGER.error(e, e.fillInStackTrace());
                e.printStackTrace();
            }
        }
        //********************* 3、所有线程执行完毕后，更新增量时间 *********************
        int pageIndexNum = platBasicDataService.saveMaxTBRQToDB("xj", serviceID, "", basicSQL, dbDataSourceID);
        return pageIndexNum;

    }


    public static String decodeBASE64(String base64Str, String encoding) {
        if (base64Str == null) {
            return null;
        }
        sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();
        try {
            byte[] b = decoder.decodeBuffer(base64Str);
            return new String(b, encoding);
        } catch (Exception e) {
            return null;
        }
    }

    public static String getBASE64(String cdaContentValue) {
        String base64Str = "";

        if (cdaContentValue == null) {
            return null;
        }

        try {
            //			String value = new String(cdaContentValue.getBytes("utf-8"), "utf-8");

            base64Str = (new sun.misc.BASE64Encoder()).encode(cdaContentValue.getBytes("utf-8")).replace("\n", "")
                    .replace("\r", "");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        return base64Str;
    }

    public static Document getDocumentFromXMLString(String messageXML, boolean isNameSpace) {
        HashMap<String, String> map = null;

        map = new HashMap<String, String>();

        if (isNameSpace) {
            //将xds注册为namesapce的名称
            map.put("xmlns", "urn:hl7-org:v3");
            map.put("xsi", "http://www.w3.org/2001/XMLSchema-instance");
            map.put("pchis", "urn:chia-org:hisc:v1");
            map.put("classCode", "DOCCLIN");
            map.put("moodCode", "EVN");
        }

        Document messageDocument = null;
        SAXReader saxReader = new SAXReader();
        //saxReader.setEncoding("utf-8");

        try {
            /*这些错误的发生是由于一些不可见的特殊字符的存在，而这些字符对于XMl文件来说又是非法的，所以XML解析器在解析时会发生异常，官方定义了XML的无效字符分为三段：
             * 0x00 - 0x08
             * 0x0b - 0x0c
             * 0x0e - 0x1f
             */
            messageXML = messageXML.replaceAll("[\\x00-\\x08\\x0b-\\x0c\\x0e-\\x1f]", "");
            /*  实体中
             *  &lt;	<	小于
             *	&gt;	>	大于
             *	&amp;	&	和号
             *	&apos;	'	单引号
             *	&quot;	"	引号
             *
             */
            messageXML = messageXML.replaceAll("\\&", "&amp;");

            saxReader.getDocumentFactory().setXPathNamespaceURIs(map);

            messageDocument = saxReader.read(new ByteArrayInputStream(messageXML.getBytes("UTF-8")));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            messageXML = null;

            saxReader = null;
        }

        return messageDocument;
    }

    public static String writeCDAToFile(Document document, String cdaFile) {
        OutputFormat format = OutputFormat.createPrettyPrint();
        format.setEncoding("UTF-8");// 设置XML文件的编码格式

        //文件夹路径
        String direPath = "G:/zhaqiang/";
        String filePath = direPath + cdaFile + ".xml";

        File direFile = new File(direPath);
        //创建文件夹
        if (!direFile.exists() && !direFile.isDirectory()) {
            try {
                direFile.mkdir();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        try {
            FileOutputStream fos = new FileOutputStream(filePath);
            //			XMLWriter xmlWriter = new XMLWriter(new FileWriter(filePath), format);
            XMLWriter xmlWriter = new XMLWriter(fos, format);
            //			String cdaStr= document.asXML();
            //			cdaStr=cdaStr.replace("type=", "xsi:type=");
            //			document=DocumentHelper.parseText(cdaStr);
            xmlWriter.write(document);
            xmlWriter.close();
        } catch (Exception e) {
            e.printStackTrace();

            return null;
        }
        return null;
    }

}
